/*********************************************************************************
 *      Copyright:  (C) 2024 Xu Jiangfeng
 *                  All rights reserved.
 *
 *       Filename:  test_esp.c
 *    Description:  This file test esp.
 *                 
 *        Version:  1.0.0(2024年07月27日)
 *         Author:  Xu Jiangfeng <1922788776@qq.com>
 *      ChangeLog:  1, Release initial version on "2024年07月27日 21时36分15秒"
 *                 
 ********************************************************************************/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#include "modbus.h"

#define REV_OK	   	     0
#define REV_WAIT	     1
#define UART_BUF         128
#define BAUD_RATE        115200
#define MODBUS_DEVICE    "/dev/ttymxc1"
#define UART_DEVICE      "/dev/ttymxc3"
#define WiFi_ID          "14pro"
#define WiFi_Pass        "0123456789"
#define MQTT_SERVER      "iot-06z00dn5q2dhe3r.mqtt.iothub.aliyuncs.com"
#define CLIENT_ID        "k151n48Urzm.Battery|securemode=2\\,signmethod=hmacsha256\\,timestamp=1722065737718|"
#define MQTT_USER        "Battery&k151n48Urzm"
#define MQTT_PASSWORD    "fe9bfda6cc214ab433e04528833057ad21a4b09e6d48f9383b100001e93f6baa"
#define MQTT_POST        "/sys/k151n48Urzm/Battery/thing/event/property/post"
#define MQTT_TOPIC       "/k151n48Urzm/Battery/user/get"
#define u8               unsigned char

unsigned short           esp8266_cnt = 0, esp8266_cntPre = 0;
unsigned char            buf[UART_BUF] = {0};

static void delay_xms(unsigned int secs)
{
	struct timeval tval;
	tval.tv_sec=secs/1000;
	tval.tv_usec=(secs*1000)%1000000;
	select(0,NULL,NULL,NULL,&tval);
}


typedef struct uart_hardware_cfg {
	unsigned int     baudrate;  
	unsigned char    dbit;
	char             parity;
	unsigned char    sbit;
}uart_cfg_t;

static struct termios old_cfg;
static int    fd;

static int uart_init(const char *device)
{
	fd = open(device, O_RDWR | O_NOCTTY); 
	if (fd < 0)
	{
		fprintf(stderr, "open error:%s:%s\n", device, strerror(errno));
		return -1;
	}

	if (0 > tcgetattr(fd, &old_cfg)) {
		fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));
		close(fd);
		return -1;
	}

	return 0;
}


static int uart_cfg(const uart_cfg_t *cfg)
{
	struct termios   new_cfg = {0};
	speed_t          speed;

	cfmakeraw(&new_cfg);

	new_cfg.c_cflag |= CREAD;

	switch (cfg->baudrate) {
		case 1200: speed = B1200;
				   break;
		case 1800: speed = B1800;
				   break;
		case 2400: speed = B2400;
				   break;
		case 4800: speed = B4800;
				   break;
		case 9600: speed = B9600;
				   break;
		case 19200: speed = B19200;
					break;
		case 38400: speed = B38400;
					break;
		case 57600: speed = B57600;
					break;
		case 115200: speed = B115200;
					 break;
		case 230400: speed = B230400;
					 break;
		case 460800: speed = B460800;
					 break;
		case 500000: speed = B500000;
					 break;
		default:
					 speed = B115200;
					 printf("default baud rate: 115200\n");
					 break;
	}

	if (0 > cfsetspeed(&new_cfg, speed)) {
		fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));
		return -1;
	}

	new_cfg.c_cflag &= ~CSIZE;
	switch (cfg->dbit) {
		case 5:
			new_cfg.c_cflag |= CS5;
			break;
		case 6:
			new_cfg.c_cflag |= CS6;
			break;
		case 7:
			new_cfg.c_cflag |= CS7;
			break;
		case 8:
			new_cfg.c_cflag |= CS8;
			break;
		default:
			new_cfg.c_cflag |= CS8;
			break;
	}

	switch (cfg->parity) {
		case 'N':
			new_cfg.c_cflag &= ~PARENB;
			new_cfg.c_iflag &= ~INPCK;
			break;
		case 'O':
			new_cfg.c_cflag |= (PARODD | PARENB);
			new_cfg.c_iflag |= INPCK;
			break;
		case 'E':
			new_cfg.c_cflag |= PARENB;
			new_cfg.c_cflag &= ~PARODD;
			new_cfg.c_iflag |= INPCK;
			break;
		default:
			new_cfg.c_cflag &= ~PARENB;
			new_cfg.c_iflag &= ~INPCK;
			break;
	}

	switch (cfg->sbit) {
		case 1:
			new_cfg.c_cflag &= ~CSTOPB;
			break;
		case 2:
			new_cfg.c_cflag |= CSTOPB;
			break;
		default:
			new_cfg.c_cflag &= ~CSTOPB;
			break;
	}

	new_cfg.c_cc[VTIME] = 0;
	new_cfg.c_cc[VMIN] = 0;

	if (0 > tcflush(fd, TCIOFLUSH)) {
		fprintf(stderr, "tcflush error: %s\n", strerror(errno));
		return -1;
	}

	if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {
		fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));
		return -1;
	}

	return 0;
}

static void io_handler(int sig, siginfo_t *info, void *context)
{
	if(SIGRTMIN != sig)
		return;

	if (POLL_IN == info->si_code) {
		if (esp8266_cnt >= sizeof(buf))
			esp8266_cnt = 0;
		esp8266_cnt = read(fd, buf, sizeof(buf));
		printf("%s\r\n", buf);
	}
}

static void async_io_init(void)
{
	struct sigaction sigatn;
	int              flag;

	flag = fcntl(fd, F_GETFL);
	flag |= O_ASYNC;
	fcntl(fd, F_SETFL, flag);
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETSIG, SIGRTMIN);

	sigatn.sa_sigaction = io_handler;
	sigatn.sa_flags = SA_SIGINFO;
	sigemptyset(&sigatn.sa_mask);
	sigaction(SIGRTMIN, &sigatn, NULL);
}

void Uart4_Init(unsigned int baud, char *device)
{
	uart_cfg_t cfg = {0};

	if (NULL == device) {
		fprintf(stderr, "Error: the device no found!\n");
		exit(EXIT_FAILURE);
	}

	if (uart_init(device))
		exit(EXIT_FAILURE);

	cfg.baudrate = baud;

	if (uart_cfg(&cfg)) {
		tcsetattr(fd, TCSANOW, &old_cfg);
		close(fd);
		exit(EXIT_FAILURE);
	}
}


void ESP8266_Clear(void)
{
	memset(buf, 0, sizeof(buf));
	esp8266_cnt = 0;
}


_Bool ESP8266_WaitRecive(void)
{
	if(esp8266_cnt == 0) 							//如果接收计数为0 则说明没有处于接收数据中，所以直接跳出，结束函数
		return REV_WAIT;

	if(esp8266_cnt == esp8266_cntPre)				//如果上一次的值和这次相同，则说明接收完毕
	{
		esp8266_cnt = 0;							//清0接收计数
		return REV_OK;								//返回接收完成标志
	}

	esp8266_cntPre = esp8266_cnt;					//置为相同
	return REV_WAIT;								//返回接收未完成标志
}

_Bool ESP8266_SendCmd(char *cmd, char *res)
{
	int    ret = 0;
	int    timeOut = 20;

	ret = write(fd, (unsigned char *)cmd, strlen((const char *)cmd));
	if (ret == 0) 
		printf("write err!\r\n");
	while(timeOut--)
	{
		if(ESP8266_WaitRecive() == REV_OK)
		{
			if(strstr((const char *)buf, res) != NULL)
			{

				ESP8266_Clear();									//清空缓存

				return 0;
			}
		}
		delay_xms(100);
	}

	return 1;

}


void send_data_to_aliyun(const char *topic, const unsigned char *data)
{
	char cmdBuf[256];

	ESP8266_Clear();
	snprintf(cmdBuf, sizeof(cmdBuf), "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", topic, data);
	ESP8266_SendCmd(cmdBuf, "OK");
}

_Bool Esp8266_Init(const char* ssid, const char* password)
{
	char  connect_cmd[256];

	ESP8266_Clear();
	printf("Initialize Esp8266(factory reset, software reset, turn off echo, test command, setting mode)\r\n");
	while (ESP8266_SendCmd("AT+RESTORE\r\n", "OK"))
		delay_xms(500);

	while (ESP8266_SendCmd("AT+RST\r\n", "OK"))
		delay_xms(500);

	while (ESP8266_SendCmd("ATE0\r\n", "OK"))
		delay_xms(500);

	while (ESP8266_SendCmd("AT\r\n", "OK"))
		delay_xms(500);

	while (ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
		delay_xms(500);
	printf("Initialize ESP8266 success!\r\n");

	printf("Connect to WiFi...\r\n");
	snprintf(connect_cmd, sizeof(connect_cmd), "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, password);
	while (ESP8266_SendCmd(connect_cmd, "OK"))
		delay_xms(2000);
	printf("WiFi connection successful!\r\n");

	printf("Connect to Aliyun...\r\n");
	snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\",\"%s\",0,0,\"\"\r\n", MQTT_USER, MQTT_PASSWORD);
	while(ESP8266_SendCmd(connect_cmd,"OK"))
		delay_xms(1000);

	snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTCLIENTID=0,\"%s\"\r\n", CLIENT_ID);
	while(ESP8266_SendCmd(connect_cmd,"OK"))
		delay_xms(1000);

	snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTCONN=0,\"%s\",1883,1\r\n", MQTT_SERVER);
	while(ESP8266_SendCmd(connect_cmd,"OK"))
		delay_xms(1000);

	snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTSUB=0,\"%s\",1\r\n", MQTT_TOPIC);
	while(ESP8266_SendCmd(connect_cmd,"OK"));
	    delay_xms(1000);
	printf("Connect to Aliyun successful, Esp8266 Init OK!\r\n");

	return 0;
}


// 读取Modbus从机数据
void read_modbus_data(modbus_t *ctx, int slave_addr, uint16_t *tab_reg, int reg_count)
{
	modbus_set_slave(ctx, slave_addr);
	int rc = modbus_read_input_registers(ctx, 0, reg_count, tab_reg);
	if (rc == -1)
	{
		fprintf(stderr, "Read failed for slave %d: %s\n", slave_addr, modbus_strerror(errno));
		return;
	}
	printf("Data from slave %d:\n", slave_addr);
	for (int i = 0; i < reg_count; i++)
	{
		printf("Reg[%d]=%d\n", i, tab_reg[i]);
	}
}


// 将整数位和小数位组合成浮点型数据
float combine_float(uint16_t int_part, uint16_t frac_part)
{
	char buffer[20];
	snprintf(buffer, sizeof(buffer), "%d.%d", int_part, frac_part);
	return strtof(buffer, NULL);
}

int main()
{
	Uart4_Init(BAUD_RATE, UART_DEVICE);
	async_io_init(); 
	while(Esp8266_Init(WiFi_ID, WiFi_Pass))
	{
		printf("Esp8266 Init Failed!!!\r\n");
	}

	// 创建modbus上下文
	modbus_t *ctx;
	uint16_t tab_reg[32];

	ctx = modbus_new_rtu(MODBUS_DEVICE, BAUD_RATE, 'N', 8, 1);
	if (ctx == NULL)
	{
		fprintf(stderr, "Unable to create the libmodbus context\n");
		return -1;
	}

	if (modbus_connect(ctx) == -1)
	{
		fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
		modbus_free(ctx);
		return -1;
	}

	while(1)
	{
		//读取每个从机的数据并上报到阿里云
		for(int slave_addr = 1; slave_addr <= 3; slave_addr++)
		{
			char json_data[256];

			read_modbus_data(ctx, slave_addr, tab_reg, 10);

			//组合为浮点型数据
			float data1 = combine_float(tab_reg[0], tab_reg[1]);
			float data2 = combine_float(tab_reg[5], tab_reg[6]);

			//构建JSON字符串
			snprintf(json_data, sizeof(json_data), "{\\\"params\\\":{\\\"voltage\\\":%.2f\\,\\\"temperature\\\":%.1f\\}\\,\\\"version\\\":\\\"1.0\\\"}", data1, data2);

			//发送数据
			send_data_to_aliyun(MQTT_POST, json_data);
			sleep(1);
		}
		sleep(5); //每5秒钟上报一次
	}

	// 关闭连接并释放资源
	modbus_close(ctx);
	modbus_free(ctx);

	tcsetattr(fd, TCSANOW, &old_cfg);
	close(fd);
	exit(EXIT_SUCCESS);
}
														
							
